﻿using System;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.TeamArchitect.PowerTools;
using Microsoft.VisualStudio.TeamArchitect.PowerTools.Features;

namespace InstantFeatureBuilder
{
    ///
    /// This class implements a link launch point which you can, without writing code
    /// or using the DSL, execute 4 common VS automation "verbs":
    ///     OpenFile        - open a file using it's default editor
    ///     VSCommand       - Execute an arbitrary Visual Studio command
    ///     CopyToClipboard - Copy the contents of a file from the Content\Data directory to the system clipboard
    ///     StartProcess    - Run a DOS command OR start the default application based on the file extension
    ///
    /// To use, add a hyperlink in one of your guidance documents like this:
    ///     launchpoint://<yourFXname>/ExecuteCommand#<cmd>|<optional-args>
    ///
    /// Example:
    ///     launchpoint://InstantFeatureExtension1/ExecuteCommand#OpenFile|*\MyClass.cs
    ///
    [LaunchPoint(Id = "ExecuteCommand")]
    public partial class ExecuteCommand : LinkLaunchPoint
    {
        EnvDTE.DTE dte;

        [ImportingConstructor]
        public ExecuteCommand(IFeatureManager featureManager)
            : base(featureManager)
        {
        }

        /// <summary>
        /// Gets and sets the UriReferenceService using MEF
        /// The UriReferenceService takes, amongst other things, content:// URIs
        /// and can retrieve either the path or contents of the specified file as desired
        /// </summary>
        [Import]
        public IFxrUriReferenceService UriReferenceService { get; set; }

        /// <summary>
        /// Gets and sets the ServiceProvider using MEF
        /// </summary>
        [Import]
        public SVsServiceProvider ServiceProvider { get; set; }

        /// <summary>
        /// Gets and sets the ISolution using MEF
        /// </summary>
        [Import]
        public ISolution Solution;

        /// <summary>
        /// Returns true if the command can be executed.
        /// </summary>
        /// <param name="feature"></param>
        /// <returns></returns>
        public override bool CanExecute(IFeatureExtension feature)
        {
            return base.CanExecute(feature);
        }

        /// <summary>
        /// Perform the command as specified by the URI "fragment" section which
        /// includes a command and then optional arguments separated by a vertical bar '|' character
        /// </summary>
        /// <param name="feature"></param>
        public override void Execute(IFeatureExtension feature)
        {
            //
            // We use a little Feature Extention Runtime magic here to retrieve the
            // current URI being processed by the Guidance Workflow web browser
            string[] parts;
            parts = FeatureCallContext.Current.GuidanceBrowserControl.CurrentLink.ToString().Split('#');

            string[] args;

            //
            // If there is no "fragment part" then we do nothing
            if (parts != null && parts.Length > 0)
            {
                args = parts[1].Split('|');

                //
                // Uncomment the line below to debug any new commands you add
                //
                //System.Windows.Forms.MessageBox.Show("There are " + args.Length + " args which are: " + parts[1]);

                dte = ServiceProvider.GetService(typeof(DTE)) as EnvDTE.DTE;
                if (dte == null)
                    return;

                //
                // Ok, here's where the URI hits the "road"
                // switch based on the "command" part
                //

                switch (args[0].ToLower())
                {
                    case "openfile":
                        OpenFile(args[1].Replace("/", "\\"));
                        break;

                    case "vscommand":
                        if (args.Length > 2)
                            VSCommand(args[1], args[2]);
                        else
                            VSCommand(args[1], null);
                        break;

                    case "startprocess":
                        if (args.Length > 2)
                            StartProcess(args[1], args[2]);
                        else
                            StartProcess(args[1], "");
                        break;

                    case "copytoclipboard":
                        CopyToClipboard(args[1]);
                        break;

                    default:
                        MessageBox.Show("Unknown command selected: " + args[0]);
                        break;
                }
            }

        }

        /// <summary>
        /// Finds a file, based on the supplied filepath and then
        /// asks VS to open it in the default editor associated with that type of file
        /// </summary>
        /// <param name="filepath">String containing file path (may include wildcards)</param>
        private void OpenFile(string filepath)
        {
            var fileItem = this.Solution.Find(filepath).FirstOrDefault();
            if (fileItem != null)
            {
                dte.StatusBar.Text = "Opening file " + fileItem.Name;
                dte.ItemOperations.OpenFile(fileItem.PhysicalPath);
                dte.StatusBar.Text = "Ready";
            }
            else
                MessageBox.Show("Unable to find a file that matches: " + filepath);
        }

        /// <summary>
        /// Execute arbitrary Visual Studio Command with optional arguments.
        /// A list of commands can be found in the Tools --> Options --> Keyboard dialog or
        /// on MSDN
        /// </summary>
        /// <param name="CommandName">Command string for Visual Studio</param>
        /// <param name="CommandArgs">Optional arguments</param>
        private void VSCommand(string CommandName, string CommandArgs)
        {
            dte.StatusBar.Text = string.Format("Executing command {0} {1}", CommandName, CommandArgs);
            if (CommandArgs != null)
            {
                dte.ExecuteCommand(CommandName, CommandArgs);
            }
            else
            {
                dte.ExecuteCommand(CommandName);
            }
            dte.StatusBar.Text = "Ready";

        }

        /// <summary>
        /// Starts a process with optional arguments.
        /// For example, to play a video, simply supply the path to the video as the file name
        /// and Windows Media Player will be started.
        /// </summary>
        /// <param name="FileName">Name of process to start or name of file to auto-start</param>
        /// <param name="Arguments">Optional list of arguments</param>
        private void StartProcess(string FileName, string Arguments)
        {
            var filePath = UriReferenceService.ResolveUri<FeatureContent>(
                new Uri(string.Format("content://./Content/Data/{0}", FileName))).Path;

            if (filePath != null)
            {
                dte.StatusBar.Text = string.Format("Starting process {0} {1}", FileName, Arguments);
                System.Diagnostics.Process.Start(filePath, Arguments);
            }
            else
            {
                dte.StatusBar.Text = string.Format("Starting process {0} {1}", FileName, Arguments);
                System.Diagnostics.Process.Start(FileName, Arguments);
            }
            dte.StatusBar.Text = "Ready";
        }

        /// <summary>
        /// Copy one of the files from the Content\Data directory to the VS clipboard
        /// and tell the user it's been done in the status bar
        /// </summary>
        /// <param name="fileToCopy">Name of the file to copy from the Content\Data directory</param>
        private void CopyToClipboard(string fileToCopy)
        {
            var txtPath = UriReferenceService.ResolveUri<FeatureContent>(
                new Uri(string.Format("content://./Content/Data/{0}", fileToCopy))).Path;
            var content = File.ReadAllText(txtPath);
            Clipboard.SetText(content);

            dte.StatusBar.Text = "Data copied to clipboard";
        }

        /// <summary>
        /// Gets the name of the binding associated with this launch point
        /// </summary>
        protected override string BindingName
        {
            //
            // Since we override the Execute() method, we don't need to return
            // anything meaningful here, so we return the empty string
            //
            get
            {
                return String.Empty;
            }
        }
    }
}
